package controller;

import java.io.IOException;
import java.lang.reflect.Type;
import java.util.Iterator;
import java.util.UUID;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.google.gson.Gson;
import com.google.gson.JsonSyntaxException;
import com.google.gson.reflect.TypeToken;

import common.Enums.DAOType;
import common.Exceptions.DAOException;
import common.Utils.CommonUtils;
import common.Utils.LinkedSet;
import common.Utils.ValidationError;

import DAO.DAOFactory;
import DAO.SessionDAO;
import MySQLConnection.MySqlDAOFactory;

import model.*;

/**
 * Servlet implementation class ScopeServlet
 */
public class ScopeServlet extends BaseServlet 
{
	private static final long serialVersionUID = 1L;
	private final String SCOPE_PAGE = "/scopeDefinition.jsp";
	
	protected void loadData(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
	{
		try
		{
			BusinessCase bc = SessionDAO.getInstance().getCachedBC();
		
			if(bc != null)
			{
				request.setAttribute("BC", bc);
				
				Solution sol = bc.getSolution();
				if(sol != null)
					request.setAttribute("Sol", sol);
				
				LinkedSet<Case> cases = new LinkedSet<Case>(); 
				cases.addAll(SessionDAO.getInstance().getBcRelatedCases(bc.getID()));
				if(cases != null)
					request.setAttribute("Cases", cases);
			}
			
			request.getRequestDispatcher(SCOPE_PAGE).forward(request, response);
			System.out.println("has been directed to: " + SCOPE_PAGE);
		}
		catch(Exception e)
		{
			StackTraceElement ste = Thread.currentThread().getStackTrace()[1];
			String location = "Class: " + ste.getClassName() + ", method: " + ste.getMethodName();
			SessionDAO.getInstance().showError(location, e.getMessage(), e);
			
			response.getWriter().write("ERROR");
		}
	}
	
	protected void verifyPendingChanges(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
	{
		Gson gson = CommonUtils.createGsonInstance(response); 
		
		try
		{
			/*boolean hasChange = false;

			BusinessCase bc = gson.fromJson(request.getParameter("BC"), BusinessCase.class);
			Type linkedSetType = new TypeToken<LinkedSet<Case>>(){}.getType();
			LinkedSet<Case> cases = gson.fromJson(request.getParameter("cases"), linkedSetType);
			
			BusinessCase originalBC = SessionDAO.getInstance().getCachedBC();
			
			if(originalBC != null && bc != null)
			{
				hasChange = CommonUtils.hasStringChange(originalBC, bc);
				
				Solution originalSol = null;
				Solution sol = null;
				if(!hasChange)
				{
					originalSol = originalBC.getSolution();
					sol = bc.getSolution();
					hasChange = (originalSol == null);
				}
				
				if(!hasChange)
					hasChange = (!originalSol.getAssumptions().equalsIgnoreCase(sol.getAssumptions()));
				if(!hasChange)
					hasChange = (!originalSol.getDescription().equalsIgnoreCase(sol.getDescription()));
				if(!hasChange)
					hasChange = (!originalSol.getOutOfScope().equalsIgnoreCase(sol.getOutOfScope()));
				if(!hasChange)
					hasChange = (!originalSol.getName().equalsIgnoreCase(sol.getName()));
				
				LinkedSet<Case> cases = null;
				if(!hasChange)
				{
					cases = new LinkedSet<Case>();
					cases.addAll(SessionDAO.getInstance().getAllCachedCases());
					hasChange = (cases == null) || cases.size() != bc.getCases().size();
				}
				
				if(!hasChange)
				{
					for(Case caso : bc.getCases())
					{
						Case originalCase = SessionDAO.getInstance().getCaseByID(caso.getID());
						hasChange = (originalCase == null);
						if(!hasChange)
							hasChange = (!originalCase.getDescription().equalsIgnoreCase(caso.getDescription()));
						
						if(hasChange)
							break;
					}
				}
			}
			else //Means that this is a new BC, and all new BC have as default 2 empty cases
				for(Case caso : bc.getCases())
				{
					if(!caso.getDescription().trim().equals(""))
					{
						hasChange = true;
						break;
					}
				}
			
			response.getWriter().write(gson.toJson(hasChange));*/
		}
		catch (Exception e) 
		{
			StackTraceElement ste = Thread.currentThread().getStackTrace()[1];
			String location = "Class: " + ste.getClassName() + ", method: " + ste.getMethodName();
			SessionDAO.getInstance().showError(location, e.getMessage(), e);
			
			response.getWriter().write(gson.toJson("ERROR"));
		}
	}
	
	protected void saveScopeData(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException 
	{
		Gson gson = CommonUtils.createGsonInstance(response);
		
		try
		{
			BusinessCase bc = gson.fromJson(request.getParameter("BC"), BusinessCase.class);
			Type linkedSetType = new TypeToken<LinkedSet<Case>>(){}.getType();  
			LinkedSet<Case> cases = gson.fromJson(request.getParameter("cases"), linkedSetType);

			String fields = "";
			if(bc.getDomain().trim().equals(""))
				fields += "Domain, ";
			
			if(bc.getAuthor().trim().equals(""))
				fields += "Author, ";
			
			if(fields.equals(""))
			{
				boolean saved = saveData(bc, cases);
				response.getWriter().write(gson.toJson(saved));
			}
			else
			{
				fields = fields.substring(0, fields.length()-2); //Removes the last unnecessary ' ,'
				ValidationError error = new ValidationError("VALIDATION", fields);
				response.getWriter().write(gson.toJson(error));
			}
		}
		catch(Exception e)
		{
			String errorType = "ERROR";
			
			if(e.getClass().equals(DAOException.class))
				errorType = "DATABASE_ERROR";
			else if(e.getClass().equals(JsonSyntaxException.class))
				errorType = "JSON_ERROR";
			
			StackTraceElement ste = Thread.currentThread().getStackTrace()[1];
			String location = "Class: " + ste.getClassName() + ", method: " + ste.getMethodName();
			SessionDAO.getInstance().showError(location, e.getMessage(), e);
			
			response.getWriter().write(gson.toJson(errorType));
		}
	}
	
	private boolean saveData(BusinessCase bc, LinkedSet<Case> cases) throws Exception
	{
		boolean retVal = false;
		DAOFactory daoFactory = DAOFactory.getDAOFactory(DAOType.MYSQL);
		daoFactory.setAutoCommit(false);
		boolean lockOwner = MySqlDAOFactory.lockConnection.tryLock(); 
		
		if(bc != null)
		{
			BusinessCase originalBC = SessionDAO.getInstance().getCachedBC();
			
			try
			{
				if(originalBC != null)
				{
					daoFactory.getSolutionDAO().update(bc.getSolution());
					originalBC.setSolution(bc.getSolution());
					
					LinkedSet<Case> originalCases = new LinkedSet<Case>();
					originalCases.addAll(SessionDAO.getInstance().getAllCachedCases());
					
					for(Case caso : cases)
					{
						for (Iterator<Case> iterator = originalCases.iterator(); iterator.hasNext();) 
						{
							Case originalCase = iterator.next();
							
							if(!cases.contains(originalCase)) //It means that the case has been REMOVED in the client side, so it must be removed from here as well.
							{
								SessionDAO.getInstance().removeCase(originalCase.getID());
								iterator.remove();
							
							}
							else if(originalCase.getID().equals(caso.getID()))
							{
								originalCase.setName(caso.getName());
								originalCase.setDescription(caso.getDescription());
								daoFactory.getCaseDAO().update(originalCase);
							}
						}
							
						if(!originalCases.contains(caso)) //It means that the case has been ADDED in the client side, so it must be added here as well.
						{
							daoFactory.getCaseDAO().insert(caso);
							originalCases.add(caso);
							
							MethodApplication metApp = new MethodApplication(UUID.randomUUID(), null, caso.getID(), null, null, null, bc.getID(), new java.util.Date());
							daoFactory.getMetAppDAO().insert(bc.getID(), metApp);
							SessionDAO.getInstance().addMethodApplication(metApp); //Add the new MetApp to the cache
						}
					}
					
					daoFactory.getBcDAO().update(bc);
				}
				else
				{
					daoFactory.getSolutionDAO().insert(bc.getSolution());
					daoFactory.getBcDAO().insert(bc);
					SessionDAO.getInstance().setCachedBC(bc);
					
					for(Case caso : cases)
					{
						daoFactory.getCaseDAO().insert(caso);
						MethodApplication metApp = new MethodApplication(UUID.randomUUID(), null, caso.getID(), null, null, null, bc.getID(), new java.util.Date());
						daoFactory.getMetAppDAO().insert(bc.getID(), metApp);
						SessionDAO.getInstance().addMethodApplication(metApp); //Add the new MetApp to the cache
					}
				}
				
				daoFactory.commitChanges();
			}
			catch(Exception e)
			{
				daoFactory.rollbackChanges();
				throw e;
			}
			finally
			{
				if(lockOwner)
				{
					MySqlDAOFactory.lockConnection.unlock();
					try { MySqlDAOFactory.getInstance().closeConnection(); } 
					catch (DAOException e) { e.printStackTrace();}
				}
			}
				
			retVal = true;
		}
		
		return retVal; 
	}
}
